using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace ChatClient
{
	class ChatClient
	{
		private ContactForm clientForm;

		private string userName;
		private TcpClient serverConnection;
		private BinaryWriter writer;
		private BinaryReader reader;
		private Thread listenerThread;

		/// <summary>
		/// Constructor de la clase. Incializa algunos atributos.
		/// </summary>
		/// <param name="userName">Nombre del usuario asociado.</param>
		public ChatClient()
		{			
			// Creamos el form y nos inscribimos en sus eventos
            clientForm = new ContactForm();
			clientForm.ConnectionRequest += new ConnectionRequestEventHandler(client_ConnectionRequire);
			clientForm.ConnectionBreakRequest += new ConnectionBreakRequestEventHandler(client_ConnectionBreakRequest);
			clientForm.SendMessage += new SendMessageEventHandler(SendMessage);			
		}

		/// <summary>
		/// Comienza la interaccion con el usuario.
		/// </summary>
		public void Start()
		{
			// Ejecutamos el formulario de interaccion con el usuario.
			Application.Run(clientForm);
		}

		/// <summary>
		/// Se ejecuta cuando el usuario desea terminar la conexion.
		/// </summary>
		private void client_ConnectionBreakRequest()
		{
			Disconnect();

			// Informamos que la conexion se termino
			clientForm.ConnectionBreak();
		}

		/// <summary>
		/// Se ejecuta cuando el usuario se desea conectar con el servidor.
		/// </summary>
		/// <param name="userName">Nombre del usuario</param>
		/// <param name="serverIp">Ip del servidor</param>
		/// <param name="port">Puerto de conexion en el servidor</param>
		private void client_ConnectionRequire(string userName, string serverIp, int port)
		{
			this.userName = userName;
			// Nos conectamos al servidor
			Connect(serverIp, port);

			// Informamos que la conexion ya esta completa
			clientForm.ConnectionReady();
		}

		/// <summary>
		/// Conecta al cliente con el servidor.
		/// </summary>
		/// <param name="serverIP">IP del servidor</param>
		/// <param name="serverPort">Puerto de conexion con el servidor.</param>
		private void Connect(string serverIP, int serverPort)
		{
            listenerThread = new Thread(new ThreadStart(ReceiveMessages));
			// Nos conectamos con el servidor.
			serverConnection = new TcpClient();
			serverConnection.Connect(IPAddress.Parse(serverIP), serverPort);

			// Creamos los objetos para comunicarnos con el servidor.
			writer = new BinaryWriter(serverConnection.GetStream());
			reader = new BinaryReader(serverConnection.GetStream());

			// Enviamos nuestro nombre de usuario
			writer.Write(userName);

			// Iniciamos el thread para escuchar al servidor
			listenerThread.Start();
		}

		/// <summary>
		/// Envia un mensaje al servidor
		/// </summary>
		/// <param name="message">Mensaje que se desea enviar.</param>
		private void SendMessage(string message)
		{
			// Enviamos el mensaje a traves del writer
			writer.Write(message);
		}

		/// <summary>
		/// Desconecta el cliente del servidor.
		/// </summary>
		private void Disconnect()
		{
			if (listenerThread.IsAlive)
			{
				try
				{
					// Terminamos el thread para escuchar
					listenerThread.Abort();
				}
				catch (ThreadAbortException)
				{
					// Evitamos que se caiga por la excepcion
				}
			}

			// Desconectamos el socket.
            if (serverConnection!= null && serverConnection.Connected)
				serverConnection.Close();
		}

		/// <summary>
		/// Recibe los mensajes que llegan desde el servidor
		/// </summary>
		private void ReceiveMessages()
		{
            try
			{
				while (true)
				{
					string completeMessage = reader.ReadString();
					// Obtenemos la ubicacion de la marca que divide el usuario con el mensaje
					int markPosition = completeMessage.IndexOf(":");
					// Separamos el usuario del mensaje
					string user = completeMessage.Substring(0, markPosition);
					string message = completeMessage.Substring(markPosition + 1);

					// Mostramos el mensaje al usuario
					clientForm.NewMessageArrive(user, message);

				}
			}
			catch (Exception)
			{
				// Capturamos las excepciones que puedan ocurrir. Simplemente terminamos
				// la conexion cuando estas ocurran
				client_ConnectionBreakRequest();
			}
            
		}
	}
}
